package com.xiaok.netty.server.config;

import com.xiaok.common.util.BeanUtils;
import com.xiaok.netty.server.channelInitializer.ServerChannelInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 创建 bean 实例的配置类
 * @author xiaok
 * @create 2020--09-30 9:50
 */
@Configuration
public class BeansConfig {


    @Value("${tcp.host}")
    private  String  tcpHost;

    @Value("${tcp.port}")
    private int tcpPort;

    @Value("${boss.thread.count}")
    private int bossCount;

    @Value("${worker.thread.count}")
    private int workerCount;

    @Value("${so.keepalive}")
    private boolean keepAlive;

    @Value("${so.backlog}")
    private int backlog;

    /*根据名称装配，防止和客户端的ChannelInitializer冲突报错*/
    @Autowired
    @Qualifier("serverChannelInitializer")
    private ServerChannelInitializer serverChannelInitializer;

    /**
     *  netty服务器启动类
     * @return
     */
    @Bean(name="serverBootstrap")
    public ServerBootstrap bootstrap(){
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup(),workerGroup())
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(serverChannelInitializer);

        Map<ChannelOption<?>, Object> tcpChannelOptions = tcpChannelOptions();
        Set<ChannelOption<?>> keySet = tcpChannelOptions.keySet();
        for (ChannelOption option : keySet) {
            serverBootstrap.option(option, tcpChannelOptions.get(option));
        }
         /*是否启用心跳保活机制。在双方TCP套接字建立连接后（即都进入ESTABLISHED状态）
        并且在两个小时左右上层没有任何数据传输的情况下，这套机制才会被激活
        */
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, keepAlive);
          /*
        在TCP/IP协议中，无论发送多少数据，总是要在数据前面加上协议头，
        同时，对方接收到数据，也需要发送ACK表示确认。
        为了尽可能的利用网络带宽，TCP总是希望尽可能的发送足够大的数据。
        这里就涉及到一个名为Nagle的算法，该算法的目的就是为了尽可能发送大块数据，避免网络中充斥着许多小数据块。
        */
        serverBootstrap.childOption(ChannelOption.TCP_NODELAY,true);

        return serverBootstrap;
    }


    private Map<ChannelOption<?>, Object> tcpChannelOptions() {
        Map<ChannelOption<?>, Object> options = new HashMap<ChannelOption<?>, Object>();


         /*
         BACKLOG用于构造服务端套接字ServerSocket对象，标识当服务器请求处理线程全满时，
        用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1，
        Java将使用默认值50
        */
        options.put(ChannelOption.SO_BACKLOG, backlog);


        return options;
    }


    /**
     * 用来监控tcp链接 指定线程数 默认是1 用默认即可
     * @return
     */
    @Bean(name="bossGroup")
    public NioEventLoopGroup bossGroup(){
        return new NioEventLoopGroup(bossCount);
    }

    /**
     * 处理io事件 一定要多线程效率高 源码中默认是cpu核数*2
     * @return
     */
    @Bean(name="workerGroup")
    public NioEventLoopGroup workerGroup(){
        return new NioEventLoopGroup(workerCount);
    }
    /**
     * 定义 TCP 连接方式地址
     * @return
     */
    @Bean(name="tcpSocketAddress")
    public InetSocketAddress inetSocketAddress(){
        return new InetSocketAddress(tcpHost,tcpPort);
    }
    /*用于创建 bean 的工厂类*/
    @Bean
    @Lazy(value=false) //关闭懒加载
    public BeanUtils beanUtils(){
         return new BeanUtils();
    }


}
